/*
 * Copyright (C) 2024 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.wire.swift

import com.squareup.wire.schema.Extend
import com.squareup.wire.schema.Field
import com.squareup.wire.schema.Schema
import com.squareup.wire.schema.SchemaHandler
import com.squareup.wire.schema.Service
import com.squareup.wire.schema.Type
import io.outfoxx.swiftpoet.FileSpec
import java.io.IOException
import okio.Path

class SwiftSchemaHandler : SchemaHandler() {
  private lateinit var generator: SwiftGenerator

  override fun handle(schema: Schema, context: Context) {
    generator = SwiftGenerator(schema, context.module?.upstreamTypes ?: mapOf())
    context.fileSystem.createDirectories(context.outDirectory)
    super.handle(schema, context)
  }

  override fun handle(type: Type, context: Context): Path? {
    if (SwiftGenerator.builtInType(type.type)) return null

    val modulePath = context.outDirectory
    val typeName = generator.generatedTypeName(type)
    val swiftFile = FileSpec.builder(typeName.moduleName, typeName.simpleName)
      .addComment(CODE_GENERATED_BY_WIRE)
      .addComment("\nSource: %L in %L", type.type, type.location.withPathOnly())
      .indent("    ")
      .apply {
        generator.generateTypeTo(type, this)
      }
      .build()

    val filePath = modulePath / "${swiftFile.name}.swift"
    try {
      context.fileSystem.write(filePath) {
        writeUtf8(swiftFile.toString())
      }
    } catch (e: IOException) {
      throw IOException(
        "Error emitting ${swiftFile.moduleName}.${typeName.canonicalName} to $modulePath",
        e,
      )
    }

    context.logger.artifactHandled(
      outputPath = modulePath,
      qualifiedName = "${swiftFile.moduleName}.${typeName.canonicalName} declared in ${type.location.withPathOnly()}",
      targetName = "Swift",
    )
    return filePath
  }

  override fun handle(service: Service, context: Context) = emptyList<Path>()
  override fun handle(
    extend: Extend,
    field: Field,
    context: Context,
  ): Path? = null

  companion object {
    private const val CODE_GENERATED_BY_WIRE = "Code generated by Wire protocol buffer compiler, do not edit."
  }
}
